#region MIT Licence
/*
 * Copyright (C) 2014 PdfCreative
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 * and associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom 
 * the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included 
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Text;

using PdfCreative.Pdf.Writer;

namespace PdfCreative.Pdf.Graphics
{
    /// <summary>
    /// The Pen class represents a pen that draws solid lines or dashed lines,
    /// according to a simple dash pattern scheme.
    /// </summary>
    public class Pen
    {
        // Private fields
        // ==============
        #region Private fields
        private Colour _colour;
        private int[] _dashPattern;
        private float _width;
        private LineCapStyle _lineCapStyle;
        private LineJoinStyle _lineJoinStyle;
        private int _start;
        #endregion



        // Object overrides
        // ================
        #region Object overrides
        /// <summary>
        /// Indicates if this pen is equal to a specified other instance.
        /// </summary>
        /// <param name="obj">The other instance to compare this pen with</param>
        /// <returns>A boolean indicating that the two instances are equal (true) or not</returns>
        public override bool Equals(object obj)
        {
            Pen otherPen = obj as Pen;
            return
                null != otherPen &&
                otherPen._colour.Equals(_colour) &&
                otherPen._dashPattern == _dashPattern &&
                otherPen._lineCapStyle == _lineCapStyle &&
                otherPen._lineJoinStyle == _lineJoinStyle &&
                otherPen._start == _start &&
                otherPen._width == _width;
        }

        /// <summary>
        /// Retrieves the hash code for the pen.
        /// </summary>
        /// <returns>The pen's hash code</returns>
        public override int GetHashCode()
        {
            int hashCode = _colour.GetHashCode();
            hashCode ^= _dashPattern.GetHashCode();
            hashCode ^= _lineCapStyle.GetHashCode();
            hashCode ^= _lineJoinStyle.GetHashCode();
            hashCode ^= _start.GetHashCode();
            hashCode ^= _width.GetHashCode();

            return hashCode;
        }
        #endregion



        // Public properties
        // =================
        #region Public properties
        /// <summary>
        /// Sets or retrieves the colour of this pen.
        /// </summary>
        public Colour Colour
        {
            get { return _colour; }
            set
            {
                if (null == value)
                    throw new ArgumentNullException("value");

                _colour = value;
            }
        }

        /// <summary>
        /// Sets or retrieves the dash pattern for this pen.
        /// </summary>
        public int[] DashPattern
        {
            get { return _dashPattern; }
            set {
                if (null == value)
                    throw new ArgumentNullException("value", "DashPattern can't be null - use an empty array if you wish to use a solid line");
                _dashPattern = value; }
        }


        /// <summary>
        /// Sets or retrieves the width of this pen.
        /// </summary>
        public float Width
        {
            get { return _width; }
            set { _width = value; }
        }

        /// <summary>
        /// Sets or retrieves the line cap style of this pen.
        /// </summary>
        public LineCapStyle LineCapStyle
        {
            get { return _lineCapStyle; }
            set { _lineCapStyle = value; }
        }

        /// <summary>
        /// Sets or retrieves the line join style of this pen.
        /// </summary>
        public LineJoinStyle LineJoinStyle
        {
            get { return _lineJoinStyle; }
            set { _lineJoinStyle = value; }
        }

        /// <summary>
        /// Sets or retrieves a value that indicates at which point in the dash pattern the stroke commences.
        /// </summary>
        public int Start
        {
            get { return _start; }
            set { _start = value; }
        }
        #endregion



        // Public methods
        // ==============
        #region Public methods
        /// <summary>
        /// Clones this pen instance.
        /// </summary>
        /// <returns>The clone.</returns>
        public Pen Clone()
        {
            Pen clone = new Pen(
                this._colour.Clone(),
                this._width,
                this._dashPattern,
                this._start);

            clone._lineCapStyle = this._lineCapStyle;
            clone._lineJoinStyle = this._lineJoinStyle;

            return clone;
        }
        #endregion



        // Internal protected methods
        // ==========================
        #region Internal protected methods
        /// <summary>
        /// Writes the changes between this pen and another pen to the specified PDF writer.
        /// </summary>
        /// <param name="writer">The writer to write to</param>
        /// <param name="otherPen">The other pen to compare this one to.</param>
        internal protected virtual void WriteChanges(PdfWriter writer, Pen otherPen)
        {
            if (false==this._colour.Equals(otherPen._colour))
                this._colour.WriteStrokeColour(writer);
            
            if(this._width!=otherPen._width)
                writer.SetLineWidth(_width);

            if(this._lineCapStyle != otherPen._lineCapStyle)
                writer.SetLineCapStyle((Writer.PdfLineCapStyle) _lineCapStyle);
            
            if(this._lineJoinStyle!=otherPen._lineJoinStyle)
                writer.SetLineJoinStyle((Writer.PdfLineJoinStyle) _lineJoinStyle);

            // See if the patterns are equal
            bool isDifferent = false;
            if (_dashPattern.Length != otherPen._dashPattern.Length) isDifferent = true;
            else for (int p = 0; p < _dashPattern.Length; p++ )
                if (_dashPattern[p] != otherPen._dashPattern[p]) { isDifferent = true; break; }

            if (true==isDifferent || this._start != otherPen._start)
                writer.SetDashPattern(
                    _dashPattern,
                    _start);
        } // WriteChanges()
        #endregion



        // Public constructor
        // ==================
        #region Public constructor
        /// <summary>
        /// Constructs a new pen with the specified colour.
        /// </summary>
        /// <param name="colour">The colour</param>
        public Pen(Colour colour)
            : this(colour, 1.0f) { }

        /// <summary>
        /// Constructs a new pen with the specified colour and width.
        /// </summary>
        /// <param name="colour">The colour</param>
        /// <param name="width">The width</param>
        public Pen(Colour colour, float width)
            : this(colour, width, new int[] { }, 0) { }

        /// <summary>
        /// Constructs a new PdfDashPattern with the specified colour, width, pattern and start position.
        /// </summary>
        /// <param name="colour">The colour</param>
        /// <param name="width">The stroking width of the pen</param>
        /// <param name="dashPattern">The dash pattern</param>
        /// <param name="start">The point in the pattern at which to start stroking</param>
        public Pen(Colour colour, float width, int[] dashPattern, int start)
        {
            if (null == colour)
                throw new ArgumentNullException("colour");

            _colour = colour;
            this.DashPattern = dashPattern;
            _width = width;
            _lineCapStyle = LineCapStyle.Butt;
            _lineJoinStyle = LineJoinStyle.Miter;
            _start = start;
        }

        /// <summary>
        /// Constructs a new PdfDashPen with the specified colour and pattern.
        /// </summary>
        /// <param name="colour">The colour</param>
        /// <param name="dashPattern">The dash pattern</param>
        public Pen(Colour colour, int[] dashPattern) :
            this(colour, 1.0f, dashPattern, 0) { }

        /// <summary>
        /// Constructs a new PdfDashPattern with the specified colour, pattern and start position.
        /// </summary>
        /// <param name="colour">The colour</param>
        /// <param name="dashPattern">The dash pattern</param>
        /// <param name="start">The point in the pattern at which to start stroking</param>
        public Pen(Colour colour, int[] dashPattern, int start) :
            this(colour, 1.0f, dashPattern, start) { }
        #endregion

    } // PdfPen class
}
